---
description: Learn how to model relational data in a Triplit schema.
---

# Relations

To define a relationship between two collections, you define a subquery that describes the relationship with `RelationMany`, `RelationOne` or `RelationById`. while `RelationOne` and `RelationById` are designed for singleton relations and will be directly nested or a sub-object or `null` if an applicable entity doesn't exist. Within a relation, either in a where clause or the `RelationById` id, parameter, you can reference the current collection's attributes with `$`.

## RelationMany

A `RelationMany` attribute will be in the shape `Array<Entity>`. It's designed to model a one-to-many relationship between two collections. If no related entities are found, the attribute will be an empty array.

In this example schema, we are modeling a school, where departments have many classes. The `departments` collection has a `classes` attribute that is a `RelationMany` to the `classes` collection.

```typescript {8-10}
import { Schema as S } from '@triplit/client';

const schema = S.Collections({
  departments: {
    schema: S.Schema({
      id: S.Id(),
      name: S.String(),
    }),
    relationships: {
      classes: S.RelationMany('classes', {
        where: [['department_id', '=', '$id']],
      }),
    },
  },
  classes: {
    schema: S.Schema({
      id: S.Id(),
      name: S.String(),
      level: S.Number(),
      building: S.String(),
      department_id: S.String(),
    }),
  },
});
```

## RelationOne

A `RelationOne` attribute will be an `Entity` or `null`. It's designed to model a one-to-one relationship between two collections. The `RelationOne` attribute will be the related entity or `null` if no related entity is found.

We can update our model of a school, so that a class has a relation to its department.

```typescript {20-22}
import { Schema as S } from '@triplit/client';

const schema = S.Collections({
  departments: {
    schema: S.Schema({
      id: S.Id(),
      name: S.String(),
    }),
    relationships: {
      classes: S.RelationMany('classes', {
        where: [['department_id', '=', '$id']],
      }),
    },
  },
  classes: {
    schema: S.Schema({
      id: S.Id(),
      name: S.String(),
      level: S.Number(),
      building: S.String(),
      department_id: S.String(),
    }),
    relationships: {
      department: S.RelationOne('departments', {
        where: [['id', '=', '$department_id']],
      }),
    },
  },
});
```

## RelationById

RelationById is a special case of `RelationOne` that is used to define a relationship by a foreign key. The `RelationById` attribute will be the related entity or `null` if no related entity is found.

We can update the previous example to use `RelationById` instead of `RelationOne`.

```typescript {20}
import { Schema as S } from '@triplit/client';

const schema = S.Collections({
  departments: {
    schema: S.Schema({
      id: S.Id(),
      name: S.String(),
    }),
    relationships: {
      classes: S.RelationMany('classes', {
        where: [['department_id', '=', '$id']],
      }),
    },
  },
  classes: {
    schema: S.Schema({
      id: S.Id(),
      name: S.String(),
      level: S.Number(),
      building: S.String(),
      department_id: S.String(),
    }),
    relationships: {
      department: S.RelationById('departments', '$department_id'),
    },
  },
});
```

## Querying collections with relations

By default, queries on collections with relations will _not_ return related data. You can use the `include` method to specify which relations you want to include in the query.

```typescript
const classesQuery = client.query('classes').Include('department');
const departmentsQuery = client.query('departments').Include('classes');
```

## Defining relations with referential variables

You can also define relations ad-hoc in a query using referential variables. This allows you to define relations that are not part of the schema and is equivalent to a `JOIN` you might see in SQL. Under the hood, this is actually what your `RelationMany`, `RelationOne`, and `RelationById` attributes are doing. See usage of referential variables in the [Variables](/query/variables#referential-variables) documentation. For example:

```typescript
// Fetch all 747 planes that have a flight to an airport newer than the plane
const query = client.query('planes').Where([
  ['model', '=', '747']
  {
    exists: client.query('flights').Where([
      ['plane_id', '=', '$1.id'],
      {
        exists: client.query('airports').Where([
          ['id', '=', '$1.destination_id']
          ['created_at', '>', '$2.created_at'],
        ]),
      },
    ]),
  },
]);
```
